package com.github.yoojia.flow.support;

import com.github.yoojia.flow.util.AppPaths;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 监控脚本文件修改
 * @author 陈小锅 (yoojiachen@gmail.com)
 * @since 1.0.0
 */
public class ScriptWatchService {

    public static final Logger LOGGER = LoggerFactory.getLogger(ScriptWatchService.class);

    private static final String SCRIPTS_DIR = "scripts";

    private final ExecutorService mScriptModifyThread = Executors.newSingleThreadExecutor();
    private final WatchService mWatchService;

    private final Map<String, OnChangedListener> mScriptListenerMap = new ConcurrentHashMap<>();

    public ScriptWatchService() {
        try {
            mWatchService = FileSystems.getDefault().newWatchService();
            final Path dir = getScriptsRootDirectory();
            if (! dir.toFile().isDirectory()){
                throw new IllegalArgumentException(dir + " is not directory!");
            }
            dir.register(mWatchService,
                    StandardWatchEventKinds.ENTRY_MODIFY);
        } catch (IOException e) {
            throw new RuntimeException("Error when create directory watch service!", e);
        }
    }

    public Path getScriptsRootDirectory() {
        return AppPaths.pathOf(SCRIPTS_DIR);
    }

    public void start(){
        mScriptModifyThread.submit(()-> {
            while (!Thread.currentThread().isInterrupted()){
                WatchKey watchKey;
                try {
                    watchKey = mWatchService.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
                for (WatchEvent<?> event : watchKey.pollEvents()){
                    final String fileName = String.valueOf(event.context());
                    final OnChangedListener listener = mScriptListenerMap.get(fileName);
                    if (listener != null) {
                        listener.onChanged();
                    }
                }
                watchKey.reset();
            }
        });
    }

    public void stop() {
        try {
            mWatchService.close();
        } catch (IOException e) { /*ignore*/}
        mScriptModifyThread.shutdown();
    }

    public void watchScript(String fileName, OnChangedListener listener) {
        mScriptListenerMap.put(fileName, listener);
    }

    public boolean isScriptExists(String fileName) {
        return mScriptListenerMap.containsKey(fileName);
    }

    ////

    public interface OnChangedListener {
        void onChanged();
    }
}
